package snake;

import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.websocket.Session;

import controller.WebSocketController;
import snake.GamePackage.GameStatus;
import snake.Player.Direction;
import snake.Player.PlayerStatus;

public class GameEngine implements Runnable
{

	private int gameId;
	private GamePackage gamePackage;
	private int rows;
	private int cols;
	private SnakePart food;
	private ServletContext servletContext;
	private Set<Session> sessionList;

	public GameEngine(int gameId, GamePackage gamePackage, ServletContext servletContext)
	{
		this.gameId = gameId;
		this.gamePackage = gamePackage;
		this.food = null;
		this.servletContext = servletContext;
		this.sessionList = Collections.synchronizedSet(new HashSet<Session>());
	}

	public void addSession(Session session)
	{
		sessionList.add(session);
	}

	public Set<Session> getSessionList()
	{
		return sessionList;
	}

	public void setSessionList(Set<Session> sessionList)
	{
		this.sessionList = sessionList;
	}

	public GamePackage getGamePackage()
	{
		return gamePackage;
	}

	private boolean checkBoundariesHit(Player player, SnakePart newPoint)
	{
		if (newPoint.x > rows || newPoint.x < 0 || newPoint.y > rows || newPoint.y < 0)
		{
			System.out.println("boundries!");
			killPlayer(player);
			return false;
		} else
		{
			return true;
		}
	}

	private void killPlayer(Player player)
	{
		
		if (player.getPlayerStatus() != PlayerStatus.DEAD)
		{
			for (int i = 0; i <= rows; i++)
			{
				for (int j = 0; j <= cols; j++)
				{
					if (gamePackage.getMatrix()[i][j] == player.getId())
					{
						
						gamePackage.setMatixValue(0, i, j);
						player.setPlayerStatus(PlayerStatus.DEAD);
					}
				}
			}
		}
	}

	private boolean checkPlayersHit(Player player, SnakePart newPoint)
	{
		if (gamePackage.getMatrix()[newPoint.x][newPoint.y] == 0)
		{
			return true;
		} else if (gamePackage.getMatrix()[newPoint.x][newPoint.y] > 0)
		{

			int otherPlayerId = gamePackage.getMatrix()[newPoint.x][newPoint.y];
			Player otherPlayer = null;
			for (Player p : gamePackage.getPlayersList())
			{
				if (p.getId() == otherPlayerId)
				{

					otherPlayer = p;
					break;
				}
			}

			boolean samePlayer = false;

			if (otherPlayer == player)
			{
				SnakePart sp = player.getSnake().getTail();

				for (int i = 0; i < player.getSnake().getSize() - 1; i++)
				{
					sp = sp.next;
				}

				if (sp.x == newPoint.x && sp.y == newPoint.y)
				{
					System.out.println("same");
					samePlayer = true;
				}
			}

			if (samePlayer == false)
			{
				SnakePart point = otherPlayer.getSnake().getHead();

				if (point.x == newPoint.x && point.y == newPoint.y)
				{
					killPlayer(otherPlayer);
					killPlayer(player);
				} else
				{
					killPlayer(player);
				}
				return false;
			} else
			{
				if (player.getDirection() == Direction.NORTH)
				{
					newPoint.x=newPoint.x+2;
					player.setDirection(Direction.SOUTH);
				} else if (player.getDirection() == Direction.EAST)
				{
					newPoint.y=newPoint.y-2;
					player.setDirection(Direction.WEST);
				} else if (player.getDirection() == Direction.SOUTH)
				{
					newPoint.x=newPoint.x-2;
					player.setDirection(Direction.NORTH);
				} else if (player.getDirection() == Direction.WEST)
				{
					newPoint.y=newPoint.y+2;
					player.setDirection(Direction.EAST);
				}
				return true;
			}
		}
		return true;
	}
	
	
	private int checkScore()
	{
		for (Player p:gamePackage.getPlayersList())
		{
			if(p.getScore()>=gamePackage.getScoreLimit())
			{
				System.out.println("player score:"+p.getScore()+"  "+gamePackage.getScoreLimit());
				return p.getId();
			}
				
		}
		return -1;
	}

	private int checkWinner()
	{
		int count = 0;
		int playerIndex = -1;
		Player player;

		for (int i = 0; i < gamePackage.getPlayersList().size(); i++)
		{
			player = gamePackage.getPlayersList().get(i);
			if (player.getPlayerStatus() == PlayerStatus.ALIVE)
			{
				playerIndex = i;
				count++;
			}

			if (count > 1)
			{
				return -2;
			}
		}

		return playerIndex;
	}

	private SnakePart getFood(Random random)
	{

		boolean ok = true;
		int x;
		int y;
		do
		{
			x = random.nextInt(rows + 1);
			y = random.nextInt(cols + 1);

			if (gamePackage.getMatrix()[x][y] == 0)
			{
				ok = true;
				gamePackage.setMatixValue(-1, x, y);
				food = new SnakePart(x, y);
			} else
			{
				ok = false;
			}

		} while (!ok);

		return food;
	}

	private boolean checkTail(Player p)
	{
		SnakePart tail = p.getSnake().getTail();
		if (p.getSnake().getEatenParts().size() > 0)
		{
			if (tail.x == p.getSnake().getEatenParts().get(0).x && tail.y == p.getSnake().getEatenParts().get(0).y)
			{

				p.getSnake().getEatenParts().remove(0);
				return true;
			} else
			{
				return false;
			}
		} else
		{
			return false;
		}

	}

	@Override
	public void run()
	{
		
		
		rows = gamePackage.getMatrix().length - 1;
		cols = gamePackage.getMatrix()[0].length - 1;
		int middleOfCol = cols / 2;
		int middleOfRow = rows / 2;

		if (gamePackage.getPlayersList().size() == 1)
		{

			Player player0 = gamePackage.getPlayersList().get(0);
			int player0Id = player0.getId();
			Snake player0Snake = player0.getSnake();

			gamePackage.setMatixValue(player0Id, 0, middleOfCol);
			gamePackage.setMatixValue(player0Id, 1, middleOfCol);
			gamePackage.setMatixValue(player0Id, 2, middleOfCol);
			player0.setDirection(Direction.SOUTH);
			player0Snake.addPart(new SnakePart(0, middleOfCol));
			player0Snake.addPart(new SnakePart(1, middleOfCol));
			player0Snake.addPart(new SnakePart(2, middleOfCol));
		} else if (gamePackage.getPlayersList().size() == 2)
		{
			Player player0 = gamePackage.getPlayersList().get(0);
			int player0Id = player0.getId();
			Snake player0Snake = player0.getSnake();

			gamePackage.setMatixValue(player0Id, 0, middleOfCol);
			gamePackage.setMatixValue(player0Id, 1, middleOfCol);
			gamePackage.setMatixValue(player0Id, 2, middleOfCol);
			player0.setDirection(Direction.SOUTH);
			player0Snake.addPart(new SnakePart(0, middleOfCol));
			player0Snake.addPart(new SnakePart(1, middleOfCol));
			player0Snake.addPart(new SnakePart(2, middleOfCol));

			Player player1 = gamePackage.getPlayersList().get(1);
			int player1Id = player1.getId();
			Snake player1Snake = player1.getSnake();

			gamePackage.setMatixValue(player1Id, rows, middleOfCol);
			gamePackage.setMatixValue(player1Id, rows - 1, middleOfCol);
			gamePackage.setMatixValue(player1Id, rows - 2, middleOfCol);
			player1.setDirection(Direction.NORTH);
			player1Snake.addPart(new SnakePart(rows, middleOfCol));
			player1Snake.addPart(new SnakePart(rows - 1, middleOfCol));
			player1Snake.addPart(new SnakePart(rows - 2, middleOfCol));

		} else if (gamePackage.getPlayersList().size() == 3)
		{
			Player player0 = gamePackage.getPlayersList().get(0);
			int player0Id = player0.getId();
			Snake player0Snake = player0.getSnake();

			gamePackage.setMatixValue(player0Id, 0, middleOfCol);
			gamePackage.setMatixValue(player0Id, 1, middleOfCol);
			gamePackage.setMatixValue(player0Id, 2, middleOfCol);
			player0.setDirection(Direction.SOUTH);
			player0Snake.addPart(new SnakePart(0, middleOfCol));
			player0Snake.addPart(new SnakePart(1, middleOfCol));
			player0Snake.addPart(new SnakePart(2, middleOfCol));

			Player player1 = gamePackage.getPlayersList().get(1);
			int player1Id = player1.getId();
			Snake player1Snake = player1.getSnake();

			gamePackage.setMatixValue(player1Id, rows, middleOfCol);
			gamePackage.setMatixValue(player1Id, rows - 1, middleOfCol);
			gamePackage.setMatixValue(player1Id, rows - 2, middleOfCol);
			player1.setDirection(Direction.NORTH);
			player1Snake.addPart(new SnakePart(rows, middleOfCol));
			player1Snake.addPart(new SnakePart(rows - 1, middleOfCol));
			player1Snake.addPart(new SnakePart(rows - 2, middleOfCol));

			Player player2 = gamePackage.getPlayersList().get(2);
			int player2Id = player2.getId();
			Snake player2Snake = player2.getSnake();

			gamePackage.setMatixValue(player2Id, middleOfRow, cols);
			gamePackage.setMatixValue(player2Id, middleOfRow, cols - 1);
			gamePackage.setMatixValue(player2Id, middleOfRow, cols - 2);
			player2.setDirection(Direction.WEST);
			player2Snake.addPart(new SnakePart(middleOfRow, cols));
			player2Snake.addPart(new SnakePart(middleOfRow, cols - 1));
			player2Snake.addPart(new SnakePart(middleOfRow, cols - 2));

		} else if (gamePackage.getPlayersList().size() == 4)
		{
			Player player0 = gamePackage.getPlayersList().get(0);
			int player0Id = player0.getId();
			Snake player0Snake = player0.getSnake();

			gamePackage.setMatixValue(player0Id, 0, middleOfCol);
			gamePackage.setMatixValue(player0Id, 1, middleOfCol);
			gamePackage.setMatixValue(player0Id, 2, middleOfCol);
			player0.setDirection(Direction.SOUTH);
			player0Snake.addPart(new SnakePart(0, middleOfCol));
			player0Snake.addPart(new SnakePart(1, middleOfCol));
			player0Snake.addPart(new SnakePart(2, middleOfCol));

			Player player1 = gamePackage.getPlayersList().get(1);
			int player1Id = player1.getId();
			Snake player1Snake = player1.getSnake();

			gamePackage.setMatixValue(player1Id, rows, middleOfCol);
			gamePackage.setMatixValue(player1Id, rows - 1, middleOfCol);
			gamePackage.setMatixValue(player1Id, rows - 2, middleOfCol);
			player1.setDirection(Direction.NORTH);
			player1Snake.addPart(new SnakePart(rows, middleOfCol));
			player1Snake.addPart(new SnakePart(rows - 1, middleOfCol));
			player1Snake.addPart(new SnakePart(rows - 2, middleOfCol));

			Player player2 = gamePackage.getPlayersList().get(2);
			int player2Id = player2.getId();
			Snake player2Snake = player2.getSnake();

			gamePackage.setMatixValue(player2Id, middleOfRow, cols);
			gamePackage.setMatixValue(player2Id, middleOfRow, cols - 1);
			gamePackage.setMatixValue(player2Id, middleOfRow, cols - 2);
			player2.setDirection(Direction.WEST);
			player2Snake.addPart(new SnakePart(middleOfRow, cols));
			player2Snake.addPart(new SnakePart(middleOfRow, cols - 1));
			player2Snake.addPart(new SnakePart(middleOfRow, cols - 2));

			Player player3 = gamePackage.getPlayersList().get(3);
			int player3Id = player3.getId();
			Snake player3Snake = player3.getSnake();

			gamePackage.setMatixValue(player3Id, middleOfRow, 0);
			gamePackage.setMatixValue(player3Id, middleOfRow, 1);
			gamePackage.setMatixValue(player3Id, middleOfRow, 2);
			player3.setDirection(Direction.EAST);
			player3Snake.addPart(new SnakePart(middleOfRow, 0));
			player3Snake.addPart(new SnakePart(middleOfRow, 1));
			player3Snake.addPart(new SnakePart(middleOfRow, 2));

		}
		//gamePackage.setGameStatus(GameStatus.);
		try
		{
			Thread.sleep(5000);
		} catch (InterruptedException e1)
		{
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		

		WebSocketController.sendMessage(gamePackage, sessionList);
		
		
		int count=0;
		
		Random random = new Random();

		for (;;)
		{
			if(count<3)
			{
				//WebSocketController.sendMessage(gamePackage, sessionList);
				try
				{
					Thread.sleep(1000);
				} catch (InterruptedException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				count++;
				WebSocketController.sendMessage(gamePackage, sessionList);
				continue;
				
			}
			else
			{
				gamePackage.setGameStatus(GameStatus.PLAYING);
			}
			
			try
			{
				Thread.sleep(100);
			} catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			if (food == null)
			{
				food = getFood(random);
			}

			SnakePart point;
			for (Player p : gamePackage.getPlayersList())
			{
				if (p.getPlayerStatus() == PlayerStatus.ALIVE)
				{
					Direction dir = p.getDirection();
					point = p.getSnake().getHead();
					SnakePart newPoint = new SnakePart(0, 0);

					if (dir == Direction.NORTH)
					{

						newPoint = new SnakePart(point.x - 1, point.y);
					} else if (dir == Direction.EAST)
					{

						newPoint = new SnakePart(point.x, point.y + 1);
					} else if (dir == Direction.SOUTH)
					{

						newPoint = new SnakePart(point.x + 1, point.y);
					} else if (dir == Direction.WEST)
					{

						newPoint = new SnakePart(point.x, point.y - 1);
					}

					if (checkBoundariesHit(p, newPoint) && checkPlayersHit(p, newPoint))
					{
						gamePackage.setMatixValue(p.getId(), newPoint.x, newPoint.y);
						p.getSnake().addPart(new SnakePart(newPoint.x, newPoint.y));

						if (newPoint.x == food.x && newPoint.y == food.y)
						{
							p.getSnake().getEatenParts().add(new SnakePart(food.x, food.y));
							p.setScore(p.getScore()+1);
							food = getFood(random);
						}

						SnakePart tail = null;
						if (!checkTail(p))
						{
							tail = p.getSnake().removePart();
							gamePackage.setMatixValue(0, tail.x, tail.y);
						}

					}

				}
			}

			int winnerIndex = checkWinner();
			if (winnerIndex == -1)
			{
				System.out.println("EGAAAAAAAAAAAAAALLLLLLLLLLLLLLLL");
				gamePackage.setGameStatus(GameStatus.FINISHED);
				WebSocketController.sendMessage(gamePackage, sessionList);
				return;
			} else if (winnerIndex >= 0)
			{
				System.out.println("WINNEEEEEEEEEEEEERRRRRRRRRRRRRRR " + winnerIndex);

				gamePackage.setGameStatus(GameStatus.FINISHED);
				WebSocketController.sendMessage(gamePackage, sessionList);
				// Thread.sleep();
				return;
			}
			
			int scoreCheck=checkScore();
			if(scoreCheck!=-1)
			{
				gamePackage.setGameStatus(GameStatus.FINISHED);
				System.out.println("WINNEEEEEEEEEEEEERRRRRRRRRRRRRRR "+scoreCheck);
				WebSocketController.sendMessage(gamePackage, sessionList);
				return;
			}

			WebSocketController.sendMessage(gamePackage, sessionList);

		}

	}

}
